/*
 * Copyright 2009 Douglas Lockamy, dlockamy_at_gmail.com
 * All rights reserved. Distributed under the terms of the MIT License.
 */
/*
    vfs spec
    
    FS File Types:
    1 Directory
    2 Text File
    3 Binary File
    4 Symlink
    5 Mount Point
    6 Union Link

    FS File Read Flags
    1 Request Type
    2 Return Array of File in Directory
    3 Read text/data file

*/

MAX_FILE_DEPTH = 100;

function jsInclude(jsFile)
{
    eval(benix.wget(jsFile) );
};

var vfsList = new Array();

function vfsNode() {
    vfsNode.Name = "";
    vfsNode.item = 0;
    vfsNode.Parent = 0;
};

message_vfsNode = vfsList[vfsList.length]=new vfsNode();
message_vfsNode.Name = 'messagefs';

proc_vfsNode = vfsList[vfsList.length]=new vfsNode();
proc_vfsNode.Name = 'procfs';


fsList = new Array();
var rootfs;

if (!this.vfs) vfs = {};

vfs.version = '0.1';

vfs.init = function()
{

    rootfs = new fileNode();
    rootfs.Name="/";
    rootfs.Type = 1;
    rootfs.prev = 0;
    rootfs.next = 0;
    rootfs.Data = new Array();
    rootfs.Parent = 0;
    
    rootfs.Data = new Array();
    rootfs.Data[0] = new fileNode();

    vfs.write('/local',1,1);
    vfs.write('/remote',1,1);
    vfs.write('/home',1,1);    
    vfs.write('/dev',1,1);    
    vfs.write('/var',1,1);
};

vfs.mount = function(serverAddress,fs_type,mountPoint,user,pass,argv)
{
    var mPath = benix.fsBuildMountPath(rootfs,mountPoint);
    var name = mountPoint.split("/");
    var aNode = vfs.walk(mPath);
    var counter = 0;

    while(vfsList[counter].Name != fs_type && counter < vfsList.length-1)
    {  
        counter++;
    }
    if(aNode.Type == 1 && vfsList[counter].Name == fs_type)
    {
        aNode.Data[aNode.Data.length] = new fileNode(); 

        var mounter = aNode.Data[aNode.Data.length - 1 ];

        if(aNode.Data.length > 1)
            aNode.Data[aNode.Data.length-2].next = mounter;
        
        var aNumber = aNode.Data.length - 1;
        
        mounter.Name=name[name.length-1];
        mounter.Type = 5;
        mounter.Data= new mountNode();
        mounter.Data[0]=fs_type;
        mounter.Data[1]=serverAddress;
        mounter.Data[2]=0;
        mounter.Data[3]=vfsList[counter].item;
        mounter.Parent = aNode;

        if(aNode.Data.length < 1)
            mounter.prev=0;
        else
            mounter.prev=aNode.Data[aNode.Data.length-2];
            
        mounter.next=0;

        return(vfsList[counter].item.mount(serverAddress,fs_type,mountPoint,user,pass,argv) );
    }
    else
        return -1;
};

vfs.umount = function(mountPoint)
{
    var aNode = vfs.walk(mountPoint);
    if(aNode.Type == 5)
    {
        if(!aNode.Data[3].umount() )
        {
            vfs.write(mountPoint,0,null);
        }
        return 0;
    }
    else  
        benix.stdout('<br>Not a mount point<br>');
};

vfs.walk = function(fsNode)
{
    return(benix.fsWalk(rootfs,fsNode) );
};

vfs.read = function(path,flags,buffer,offset,length,sessionKey)
{

    if(flags =='1')
    {
        var readNode = vfs.walk(path);
        
        if(readNode.Type == 1)
            return 1;
        if(readNode.Type==4)
        {
            return(vfs.read(readNode.Data,1,null,null,null,sessionKey) );
        } 
        if(readNode.Type==5)
        {
            var loadPath = benix.parsePath(readNode,path);
            if(loadPath == '/')
                return 1;
            else
                return(readNode.Data[3].read(loadPath,flags,buffer,offset,length,sessionKey) );
        }  
        if(readNode.Type==6)
        {
            var loadPath = benix.parsePath(readNode,path);
            return(vfs.read(readNode.Data[0]+loadPath,flags,buffer,offset,length,sessionKey) );
        } 
        else
            return 0;
    }
    if(flags =='2')
    {    
        var readNode = vfs.walk(path);
        
        if(readNode.Type == 1)
            return readNode;
        if(readNode.Type==4)
        {
            var loadPath = benix.parsePath(aNode,path);
            return(vfs.read(readNode.Data+loadPath,2,buffer,offset,length,sessionKey) );
        } 
        if(readNode.Type==5)
        {
            var loadPath = benix.parsePath(aNode,path);
            return(readNode.Data[3].read(loadPath,flags,buffer,offset,length,sessionKey) );
        }    
        if(readNode.Type==6)
        {
            var loadPath = benix.parsePath(readNode,path);
            return(vfs.read(readNode.Data[0]+loadPath,2,buffer,offset,length,sessionKey) );
        } 
        else
            return readNode;
    }
    if(flags == '3' || flags == '4' )
    {  
        var readNode = vfs.walk(path);
        
        if(readNode.Type == 1)
        {
            if(readNode.Data[0].Name)
            return readNode.Data[0].Name;
            else
            return "File Read Error"
        }
        if(readNode.Type==4)
        {
            var loadPath = benix.parsePath(readNode,path);        
            return(vfs.read(readNode.Data+loadPath,flags,buffer,offset,length,sessionKey) );
        } 
        if(readNode.Type==5)
        {
            var loadPath = benix.parsePath(readNode,path);        
            return(readNode.Data[3].read(loadPath,flags,buffer,offset,length,sessionKey) );
        } 
        if(readNode.Type==6)
        {
            var loadPath = benix.parsePath(readNode,path);
            return(vfs.read(readNode.Data[0]+loadPath,2,buffer,offset,length,sessionKey) );
        } 
        else
            return readNode.Data;    
    }
    else
        return -1;
};

vfs.findMountPoint = function(path)
{
    return('/local');
};

vfs.write = function(path,flags,buffer,offset,length,sessionKey)
{
/*
    FS File Write Flags
    0 Unlink file
    1 Create Directory
    2 Create Data File
    3 Create Link File*/
    var workPath = '/';
    var aNode = 0;
    var pathArray = path.split('/');
    var counter=0;
    
    while(counter <= pathArray.length-2)
    {
        if(workPath == '/')
            workPath = workPath + pathArray[counter];
        else
            workPath = workPath + '/' + pathArray[counter];
        counter++;
    }

    if(flags==0)
    {
        //unlink a node
            counter = 0;
            aNode = vfs.walk(path);
            
            if(aNode.Type == 5)
            {
                var loadPath = benix.parsePath(aNode,path);
                return(aNode.Data[3].write(loadPath,flags,buffer,offset,length,sessionKey) );
            };
            if(aNode.Type == 4)
            {
                fileStruct = path.split('/');
                remPath = aNode.Data+"/"+fileStruct[fileStruct.length-1];
                return(vfs.write(remPath,flags,buffer,offset,length,sessionKey) );
            };
            if(aNode.Type == 6)
            {
                fileStruct = path.split('/');              
                var counter = 0;
                alert("remove dir from union mount "+aNode.Data.length );
                while(counter < aNode.Data.length)
                {
                    remPath = aNode.Data[counter]+"/"+fileStruct[fileStruct.length-1];
                    vfs.write(remPath,flags,buffer,offset,length,sessionKey)
                    counter++;
                };
                return 0;
            };
            var prevNode = aNode.prev;   
            var nextNode = aNode.next;      

            while(aNode.Parent.Data[counter].Name != aNode.Name && counter < aNode.Parent.Data.length)
            {
                counter++;
            }
            
            aNode.Parent.Data.splice(counter,1);
            
            prevNode.next = nextNode;
            nextNode.prev = prevNode;
    }
    
    if(flags==1)
    {

//        if(vfs.walk(path) != -1)
  //          return -1;

        aNode = vfs.walk(workPath);
        if(aNode.Type == 1 && buffer == 1 )
        {
            aNode.Data[aNode.Data.length] = new fileNode;
            var newDir = aNode.Data[aNode.Data.length-1];
            newDir.Name = pathArray[pathArray.length-1];
            newDir.Type = 1;
            newDir.next = 0;
            newDir.Data = new Array();
            newDir.prev = aNode.Data[aNode.Data.length-1];
            newDir.Parent = aNode;

            if(aNode.Data.length >= 2 )
            {        
                aNode.Data[aNode.Data.length-2].next=newDir;
            }   
            return 0;       
        }
        else if(aNode.Type==4)
        { 
            return(vfs.write(aNode.Data+"/"+pathArray[pathArray.length-1],flags,buffer,offset,length,sessionKey) );
        }
        
        else if(aNode.Type==5 )
        {
            var loadPath = benix.parsePath(aNode,path);

            return(aNode.Data[3].write(loadPath,flags,buffer,offset,length,sessionKey) );
        }    
        else if(aNode.Type==6)
        {
            var loadPath = benix.parsePath(aNode,path);
            var count = 0;
            while(count < aNode.Data.length)
            {
                vfs.write(aNode.Data[count]+loadPath,flags,buffer,offset,length,sessionKey);
                count++;
            }
            return( 0  );
        }  
        else
            return -1;
            
    }
    else if(flags==2)
    {
       // if(vfs.walk(path) != -1)
         //   return -1;
        aNode = vfs.walk(workPath);

        if(aNode.Type==1 )
        {
            var newfile = aNode.Data[aNode.Data.length] = new fileNode();
            newfile.Name = pathArray[pathArray.length-1];
            newfile.Type = 2;
            newfile.next = 0;
            newfile.Data = buffer;
            newfile.prev = aNode.Data[aNode.Data.length-1];
            
            if(aNode.Data.length >= 2 )
            {        
                aNode.Data[aNode.Data.length-2].next=newfile;
            }           
        }
        
        if(aNode.Type==5 )
        {
            var loadPath = benix.parsePath(aNode,path);

            return(aNode.Data[3].write(loadPath,flags,buffer,offset,length,sessionKey) );
        }  
        
        if(aNode.Type == 6 )
        {
            var loadPath = benix.parsePath(aNode,path);

            var counter = 0;

            while(counter < aNode.Data.length)
            {
                vfs.write(aNode.Data[counter]+loadPath,flags,buffer,offset,length,sessionKey);
                counter++;
            }
            
            return 0;
        } 
        
        else
            return -1;
    }
    else if(flags==3)
    {
        aNode = vfs.walk(workPath);
 //      if(vfs.walk(buffer))
            var bNode = vfs.walk(buffer);
   //     else 
     //       return -1;
       if(bNode.Type == 1 || bNode.Type == 5)
        {
            if(aNode.Type == 1 )
            {
                aNode.Data[aNode.Data.length] = new fileNode;
                var newDir = aNode.Data[aNode.Data.length-1];
                newDir.Name = pathArray[pathArray.length-1];
                newDir.Type = 4;
                newDir.next = 0;
                newDir.Data = buffer;
                newDir.prev = aNode.Data[aNode.Data.length-1];
                newDir.Parent = aNode;

                if(aNode.Data.length >= 2 )
                {        
                    aNode.Data[aNode.Data.length-2].next=newDir;
                } 
                return 0;         
            }
        
            else if(aNode.Type==5 )
            {
                var loadPath = benix.parsePath(aNode,path);
                return(aNode.Data[3].write(loadPath,flags,buffer,offset,length,sessionKey) );
            }  
                
            else
                return -1;
        }
        if(bNode.Type == 5)
        {
            if(aNode.Type==1 )
            {
                var newfile = aNode.Data[aNode.Data.length] = new fileNode();
                newfile.Name = pathArray[pathArray.length-1];
                newfile.Type = 4;
                newfile.next = 0;
                newfile.Data = buffer;
                newfile.prev = aNode.Data[aNode.Data.length-1];
            
                if(aNode.Data.length >= 2 )
                {        
                    aNode.Data[aNode.Data.length-2].next=newfile;
                }           
            }
        
            if(aNode.Type==5 )
            {
                var loadPath = benix.parsePath(aNode,path);
                return(aNode.Data[3].write(loadPath,flags,buffer,offset,length,sessionKey) );
            }  
        
            else
                return -1;       
        }   

        else 
            return bNode.Type;
    }
    else if(flags==4)
    {
        //create a union link//
        //but only supported under /dev
        if(vfs.walk(path)==-1)
        {
            aNode = vfs.walk(workPath);

            if(aNode.Name != "dev")
                return -1;

            if(aNode.Type==1 )
            {
                aNode.Data[aNode.Data.length] = new fileNode;
                var newDir = aNode.Data[aNode.Data.length-1];
                newDir.Name = pathArray[pathArray.length-1];
                newDir.Type = 6;
                newDir.next = 0;
                newDir.Data = new Array();
                newDir.prev = aNode.Data[aNode.Data.length-1];
                newDir.Parent = aNode;

                if(aNode.Data.length >= 2 )
                {        
                    aNode.Data[aNode.Data.length-2].next=newDir;
                }   
                newDir.Data[newDir.Data.length] = buffer;          
            }
        
            if(aNode.Type==5 )
            {
                var loadPath = benix.parsePath(aNode,path);
                return(aNode.Data[3].write(loadPath,flags,buffer,offset,length,sessionKey) );
            }  
        
            else
                return -1;
        }
        else 
        {
            aNode = vfs.walk(workPath);

            if(aNode.Name != "dev")
                return -1;
                
            aNode = vfs.walk(path);

            aNode.Data[aNode.Data.length] = buffer;          

            return 0;
        }
    } 
    
    else if(flags==5)
    {
        if(vfs.walk(path) )
        {
            aNode = vfs.walk(path);

            if(aNode.Type == 5)
            {
                return -1;
            }
            if(aNode.Type == 6)
            {
                aNode.Data[aNode.Data.length] = buffer;
                return 0;
            }
            else
                aNode.Data = buffer;
        }
        else
            return -1;
    }
    
    else 
        return -1;

};

vfs.stat = function(path)
{
    var aNode = vfs.walk(path);
    benix.stdout(aNode.Type + '<br>');
    benix.stdout(aNode.Data.length+ '<br>' );
};
